#ifndef ank1_h
#define ank1_h

// this header needs
#include "smg.h"

// ANK1 section of BCK files of Super Mario Galaxy

// structure
// magic - 32 bits ("ANK1" or "1KNA")
// size - 32 bits
// loop_mode - 8 bits (from 0 - 4)
// rot_lshift - 8 bits
// anim_length - 16 bits
// bone_count - 16 bits
// scale_arr_length - 16 bits (array f32)
// rot_arr_length - 16 bits (array int16)
// transl_arr_length - 16 bits (array f32)
// bone_anim_data_offset - 32 bits
// scale_arr_offset - 32 bits
// rot_arr_offset - 32 bits
// transl_arr_offset - 32 bits
// pad1 - aligns to the nearest 32 byte multiple
// --- repeated bone_count times
//   --- repeated 9 times (xyz scale/rot/trans components)
//     keyframe_count - 16 bits
//     start_index - 16 bits
//     tangent_mode - 16 bits
//   --- end
// --- end
// pad2 - aligns to the nearest 32 byte multiple
// --- repeated scale_arr_length times
//   number - (32 bits) interpreted as a float32?
// --- end
// pad3 - aligns to the nearest 32 byte multiple
// --- repeated rot_arr_length times
//   number - (16 bits) interpreted as an int16? 0 - 7fff (0 to 180 degs) 8000 - FFFF (-180 to 0 degs)
// --- end
// pad4 - aligns to the nearest 32 byte multiple
// --- repeated transl_arr_length times
//   number - (32 bits) interpreted as a float32?
// --- end
// pad5 - aligns to the nearest 32 byte multiple

// ank1 constants
#define OWL_SMG_ANK1_COMP_LENGTH  9
#define OWL_SMG_ANK1_X_SCALE_POS  0
#define OWL_SMG_ANK1_X_ROT_POS    1
#define OWL_SMG_ANK1_X_TRANSL_POS 2
#define OWL_SMG_ANK1_Y_SCALE_POS  3
#define OWL_SMG_ANK1_Y_ROT_POS    4
#define OWL_SMG_ANK1_Y_TRANSL_POS 5
#define OWL_SMG_ANK1_Z_SCALE_POS  6
#define OWL_SMG_ANK1_Z_ROT_POS    7
#define OWL_SMG_ANK1_Z_TRANSL_POS 8

#define OWL_SMG_ANK1_MAGIC_LENGTH 4
owl_umax OWL_SMG_ANK1_MAGIC[][2] = {{0x414E4B31, 0x314B4E41}}; // "ANK1", "1KNA" (int form, easier for checks)

#define OWL_SMG_ANK1_BONE_ANIM_DATA_OFFSET 0x40
#define OWL_SMG_ANK1_PAD1_SIZE 28

typedef enum {
  OWL_SMG_ANK1_INTERP_SOFT,
  OWL_SMG_ANK1_INTERP_CUSTOM,
}owl_smg_ank1_interp;

// main information (most relevant information)
typedef struct {                      // size on binary file
  owl_byte loop_mode;                 // 8bits
  owl_byte rot_lshift;                // 8bits
  owl_umax anim_length;               // 16bits
  owl_umax bone_count;                // 16bits
  owl_umax scale_arr_length;          // 16bits
  owl_umax rot_arr_length;            // 16bits
  owl_umax transl_arr_length;         // 16bits
  struct owl_smg_ank1_bone_anim_data {    
    owl_umax kf_count;                // 16bits
    owl_umax data_index;              // 16bits
    owl_smg_ank1_interp interp_mode;  // 16bits
  } ** bone_anim_data;                // (3 * 16bits) * bone_count
  // ^ treated as an array of pointers to arrays of 
  // (struct owl_smg_ank1_bone_anim_data)s of size OWL_SMG_ANK1_COMP_LENGTH
  owl_fmax * scale_arr;               // seems to always correspond to 32bits * scale_arr_length
  owl_smax * rot_arr;                 // seems to always correspond to 16bits * rot_arr_length
  owl_fmax * transl_arr;              // seems to always correspond to 32bits * trans_arr_length
} owl_smg_ank1_main;

// extra information (can be determined from main information)
typedef struct {                  // size on binary file
  owl_umax magic;                 // 32bits ("ANK1" or "1KNA")
  owl_umax size;                  // 32bits
  owl_umax bone_anim_data_offset; // 32bits
  owl_umax scale_arr_offset;      // 32bits
  owl_umax rot_arr_offset;        // 32bits
  owl_umax transl_arr_offset;     // 32bits
  owl_byte pad1_size;             // aligns the next data to the next 32 byte position multiple
  owl_byte pad2_size;             // aligns the next data to the next 32 byte position multiple
  owl_byte pad3_size;             // aligns the next data to the next 32 byte position multiple
  owl_byte pad4_size;             // aligns the next data to the next 32 byte position multiple
  owl_byte pad5_size;             // aligns the next data to the next 32 byte position multiple
} owl_smg_ank1_extra;

// the actual structure
typedef struct {
  owl_smg_ank1_main * main;
  owl_smg_ank1_extra * ext;
} owl_smg_ank1;

// enum with the possible codes a ANK1 section will present while analizing it
// all errors are named to indicate the place of the read error (in the best case)
// codes ending in 1 mean that are errors that come from analizing a ANK1 section alone
// codes ending with 2 are errors that come from analizing the section agaisnt the sections from a BMD/BDL
// other errors are related to logic errors when reading the raw string
typedef enum
{
  OWL_SMG_ANK1_ERR_ALL_GOOD,
  
  OWL_SMG_ANK1_ERR_FUNC_PARAMS,
  OWL_SMG_ANK1_ERR_STR_SIZE,
  OWL_SMG_ANK1_ERR_MEM_ALLOC,
  
  OWL_SMG_ANK1_ERR_MAGIC_1,
  OWL_SMG_ANK1_ERR_SIZE_1,
  OWL_SMG_ANK1_ERR_LOOP_MODE_1,
  OWL_SMG_ANK1_ERR_BONE_ANIM_DATA_OFFSET_1,
  OWL_SMG_ANK1_ERR_SCALE_ARR_OFFSET_1,
  OWL_SMG_ANK1_ERR_ROT_ARR_OFFSET_1,
  OWL_SMG_ANK1_ERR_TRANSL_ARR_OFFSET_1,
  OWL_SMG_ANK1_ERR_PAD1_1,
  OWL_SMG_ANK1_ERR_BONE_ANIM_DATA_1,
  OWL_SMG_ANK1_ERR_PAD2_1,
  OWL_SMG_ANK1_ERR_SCALE_ARR_1,
  OWL_SMG_ANK1_ERR_PAD3_1,
  OWL_SMG_ANK1_ERR_ROT_ARR_1,
  OWL_SMG_ANK1_ERR_PAD4_1,
  OWL_SMG_ANK1_ERR_TRANSL_ARR_1,
  OWL_SMG_ANK1_ERR_PAD5_1,
  
  OWL_SMG_ANK1_ERR_BONE_COUNT_2,
  
  OWL_SMG_ANK1_ERR_LIMIT,
} owl_smg_ank1_err_cd;

// global variable for the owl_create_smg_ank1()
// function used by the owl_get_err_cd_smg_ank1()
owl_smg_ank1_err_cd OWL_SMG_ANK1_ERR_CD = OWL_SMG_ANK1_ERR_ALL_GOOD;
owl_umax OWL_SMG_ANK1_ERR_READ_POS = 0;

// basic functions
owl_bool owl_check_smg_ank1_main_pointer(owl_byte * src, owl_umax size);
owl_bool owl_check_smg_ank1_ext_pointer(owl_byte * src, owl_umax size);
owl_bool owl_check_smg_ank1_pointer(owl_byte * src, owl_umax size);
owl_bool owl_check_smg_ank1_main_members(owl_byte loop_mode,
                                         owl_byte rot_lshift,
                                         owl_umax anim_length,
                                         owl_umax bone_count,
                                         owl_umax scale_arr_length,
                                         owl_umax rot_arr_length,
                                         owl_umax transl_arr_length,
                                         struct owl_smg_ank1_bone_anim_data ** bone_anim_data,
                                         owl_fmax * scale_arr,
                                         owl_smax * rot_arr,
                                         owl_fmax * transl_arr);
owl_bool owl_check_smg_ank1_main_all(owl_smg_ank1_main * main, owl_umax size);
owl_bool owl_check_smg_ank1_all(owl_smg_ank1 * ank1, owl_umax size);
owl_smg_ank1 * owl_create_smg_ank1(owl_str * raw, owl_endian endian);
owl_umax owl_free_smg_ank1(owl_smg_ank1 * ank1);
owl_umax owl_print_smg_ank1(owl_byte * src, owl_umax size);
owl_char owl_comp_smg_ank1(owl_byte * ank11, owl_byte * ank12);

// specific functions
owl_bool owl_get_smg_ank1_extra(owl_smg_ank1 * ank1);
owl_umax owl_get_smg_ank1_bin_size(owl_smg_ank1 * ank1);
owl_smg_ank1_err_cd owl_get_err_cd_smg_ank1(owl_str * raw, owl_endian endian);
owl_smg_ank1 * owl_get_smg_ank1_min_args(owl_byte loop_mode,
                                         owl_byte rot_lshift,
                                         owl_umax anim_length,
                                         owl_umax bone_count,
                                         owl_umax scale_arr_length,
                                         owl_umax rot_arr_length,
                                         owl_umax transl_arr_length,
                                         struct owl_smg_ank1_bone_anim_data ** bone_anim_data,
                                         owl_fmax * scale_arr,
                                         owl_smax * rot_arr,
                                         owl_fmax * transl_arr);
owl_str * owl_get_smg_ank1_as_str(owl_smg_ank1 * ank1, owl_endian endian);

#include "ank1.c"

#endif // ank1_h
